package com.espirit.eap.manager;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.espirit.eap.ValidationUtil;
import com.espirit.eap.sa.User;
import com.espirit.eap.sa.UserServiceSupport;
import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.struts.spring.CacheService;
import com.googlecode.cswish.struts.spring.GenericService;
import com.googlecode.cswish.util.ClassUtil;

@Service
public class ProofNbrRuleManager {
	@Resource
	GenericService genericService;
	
	@Resource
	ValidationUtil genericServiceUtil;
	
	@Resource
	NbrRuleSourceManager nbrRuleSourceManager;
	
	@Resource
	UserServiceSupport userService;
	
	@Resource
	SystemDataService systemDataService;
	
	@Resource
	CacheService cacheService;
	
	private  Map<String,ProofNbrRule> rules ;
	private Map<String , String> yearCache;
	
	@PostConstruct
	private void init() {
		rules = cacheService.newCache(ProofNbrRule.class);
		yearCache = cacheService.newCache(String.class);
	}
	
	public synchronized void onChanged(ProofNbrRule orderNbrRule){
		if (orderNbrRule == null) return ;
		String key = orderNbrRule.getModelName();
		ProofNbrRule model = rules.get(key);
		if (model != null){
			model = genericService.load(model);
		} else {
			model = new ProofNbrRule();
			model.setModelName(key);
			model = genericService.searchAll(model).getData(0);
		}
		if (model != null){
			model.setPrefix1(genericService.load(model.getPrefix1()));
			model.setPrefix2(genericService.load(model.getPrefix2()));
			model.setPrefix3(genericService.load(model.getPrefix3()));
			model.setPrefix4(genericService.load(model.getPrefix4()));
			model.setPrefix5(genericService.load(model.getPrefix5()));
			model.setPrefix6(genericService.load(model.getPrefix6()));
			model.setPrefix7(genericService.load(model.getPrefix7()));
			model.setPrefix8(genericService.load(model.getPrefix8()));
			rules.put(key, model);
		}
	}
	
	public synchronized String getNextNbr(ProofNbrAware nbrAware ,ProofNbrAware oldNbrAware) {
		String key = ClassUtil.getSimpleClassName(nbrAware);
		ProofNbrRule model = rules.get(key);
		if (model == null){
			model = new ProofNbrRule();
			model.setModelName(key);
			model = (ProofNbrRule) genericServiceUtil.searchAll(model).getData(0);
			if (model != null){
				rules.put(key, model);
			}
		}
		if (model != null){
			 Date now = new Date();
		 	 ProofNbr orderNbr = new ProofNbr();
			 orderNbr.setModelName(nbrAware.getClass().getSimpleName());
			 ProofNbr existed = (ProofNbr) genericServiceUtil.searchAll(orderNbr).getData(0);
			 if (nbrAware.getProofNbr() == null){
				 String date = new SimpleDateFormat("yyyyMMdd").format(now);
				 if (existed == null){
					 orderNbr.setDate(date);
					 orderNbr.setDayNbr(1);
					 orderNbr.setMonthNbr(1);
					 orderNbr.setYearNbr(1);
					 existed = genericService.add(orderNbr);
				 } else {
					 String dbDate = existed.getDate();
					 if (date.equals(dbDate)){
						 existed.setDayNbr(existed.getDayNbr() + 1);
						 existed.setMonthNbr(existed.getMonthNbr() + 1);
						 existed.setYearNbr(existed.getYearNbr() + 1);
						 genericService.merge(existed);
					 } else {
						 if (!dbDate.substring(0, 4).equals(date.substring(0,4))){
							 existed.setDate(date);
							 existed.setDayNbr(1);
							 existed.setMonthNbr(1);
							 existed.setYearNbr(1);
							 genericService.merge(existed);
						 } else if (!dbDate.substring(4,6).equals(date.substring(4, 6))){
							 existed.setDate(date);
							 existed.setDayNbr(1);
							 existed.setMonthNbr(1);
							 existed.setYearNbr(existed.getYearNbr() +1);
							 genericService.merge(existed);
						 } else if (!dbDate.substring(6,8).equals(date.substring(6, 8))){
							 existed.setDate(date);
							 existed.setDayNbr(1);
							 existed.setMonthNbr(existed.getMonthNbr() + 1);
							 existed.setYearNbr(existed.getYearNbr() +1);
							 genericService.merge(existed);
						 }
					 }
				 }
			 }
			 NbrRuleSource [] prefixes = new NbrRuleSource[]{model.getPrefix1(),
					 model.getPrefix2(),
					 model.getPrefix3(),
					 model.getPrefix4(),
					 model.getPrefix5(),
					 model.getPrefix6(),
					 model.getPrefix7(),
					 model.getPrefix8()};
			 
			 StringBuilder sb = new StringBuilder();
			 for(NbrRuleSource prefix : prefixes){
				 setPrefix(sb ,nbrAware , oldNbrAware ,model, prefix);
			 }
			 
			 setNbr(sb, model, existed,prefixes ,nbrAware);	 
			 return sb.toString();
		} else {
			return null;
		}
	}
	
	private void setPrefix(StringBuilder sb ,ProofNbrAware nbrAware ,ProofNbrAware oldNbrAware,ProofNbrRule orderNbrRule,NbrRuleSource nbrRuleSource){
		 if (nbrRuleSource != null){
			 Date date = new Date();
			 if (nbrAware.getProofNbr() != null){
				 date = oldNbrAware.getCreatedOn();
			 }
			 if (nbrRuleSource.isPrefix()){
				 sb.append(orderNbrRule.getPrefix());
			 } else if (nbrRuleSource.isYear()){
				 String year = new SimpleDateFormat("yyyy").format(date);
				 String yearPrefix = yearCache.get(year);
				 if (yearPrefix == null){
					 String startYear = systemDataService.getValue(SystemData.START_YEAR);
					 if (startYear == null){
						 Calendar rightNow = Calendar.getInstance();
						 rightNow.setTime(new Date());
						 Integer curYear = rightNow.get(Calendar.YEAR);
						 startYear = curYear.toString();
						 systemDataService.setValue(SystemData.START_YEAR, startYear);
					 }
					 if (startYear != null){
						 int gap = Integer.valueOf(year) - Integer.valueOf(startYear);
						 yearPrefix = String.valueOf((char)('A' + gap));
					 } else {
						 throw new ApplicationException("system setting error: start year not setted! please contact admin!");
					 }
					 yearCache.put(year, yearPrefix);
				 }
				 sb.append(yearPrefix);
			 } else if (nbrRuleSource.isMonth()){
				 String month = new SimpleDateFormat("MM").format(date);
				 sb.append(month);
			 } else if (nbrRuleSource.isDay()){
				 String day = new SimpleDateFormat("dd").format(date);
				 sb.append(day);
			 } else if (nbrRuleSource.isUserId()){
				 if (oldNbrAware == null){
					 sb.append(userService.getLoginUser().getCode());
				 } else {
					 User user = oldNbrAware.getCreated();	 
					 if (user != null){
						 sb.append(user.getCode());
					 } else {
						 sb.append(userService.getLoginUser().getCode());
					 }
				 }
			 }
		 }
	}
	
	private void setNbr(StringBuilder sb ,ProofNbrRule orderNbrRule,ProofNbr orderNbr ,NbrRuleSource[] prefixes ,ProofNbrAware nbrAware){
		if (nbrAware.getProofNbr() != null){
			int len = nbrAware.getProofNbr().length();
			Integer digit = orderNbrRule.getDigit();
			String nbr = nbrAware.getProofNbr().substring(len - digit);
			sb.append(nbr);
		} else {
			boolean isYear = false;
			boolean isMonth = false;
			boolean isDay = false;
			for (NbrRuleSource nbrRuleSource : prefixes){
				 if (nbrRuleSource == null) continue ;
				 if (nbrRuleSource.isYear()){
					 isYear = true;
				 } else if (nbrRuleSource.isMonth()){
					 isMonth = true;
				 } else if (nbrRuleSource.isDay()){
					 isDay = true;
				 }
			}
			Integer nbr = null;
			if (isYear && isMonth && isDay){
				nbr = orderNbr.getDayNbr();
			} 
			if (isYear && isMonth && !isDay){
				nbr = orderNbr.getMonthNbr();
			}
			if (isYear && !isMonth && !isDay){
				nbr = (orderNbr.getYearNbr());
			}
			if (nbr != null){
				Integer digit = orderNbrRule.getDigit();
				if (digit == null){
					digit = 2;
				}
				DecimalFormat   format = new   DecimalFormat( StringUtils.repeat("0", digit));
	//			sb.append("-");
				sb.append(format.format(nbr));
			}
		}
	}
}
